<template>
  <div ref="canvas"></div>
</template>

<script lang="ts" setup>
import * as THREE from 'three';
import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls';
import { ref, onMounted } from "vue";
// 该示例层级结构为:
//         scene(小区)
//           |
//         group(小区房子)
//          /         \
//      group(高层)    group(洋房)
//      /   /           \   \
//    1号楼 2号楼 ...   6号楼 7号楼 ...
//
// 在 ThreeJs 中每个模型都有它自己的本地坐标和世界坐标:
// 本地坐标: 以父层级为参照的坐标信息，在模型中心点 ( 可以理解成在建模时，基于建模原点生成的一个坐标数据 )
// 世界坐标: 以整个scene场景为参照的坐标 ( 相机和模型默认是在世界坐标的原点 )
//         是由局部坐标加上父级的变化场景：平移（position）、旋转（rotation）、缩放（scale）计算后得出。
//  1. 改变子对象的.position，子对象在3D空间中的坐标会发生改变。
//  2. 改变父对象的.position，子对象在3D空间中的位置也会跟着变化，也就是说父对象.position和子对象.position叠加才是才是子对象的.position。
//  3. 任何一个模型的本地坐标(局部坐标)就是模型的.position属性。
//  4. 一个模型的世界坐标，说的是，模型自身.position和所有父对象.position累加的坐标。

// 声明一个三维向量用来表示某个坐标
// const worldPosition = new THREE.Vector3();
// 获取mesh的世界坐标，你会发现mesh的世界坐标受到父对象group的.position影响
// mesh.getWorldPosition(worldPosition);
// console.log('世界坐标',worldPosition);
// console.log('本地坐标',mesh.position);

// 而对于用户在屏幕上的操作，我们能获取的只能是屏幕坐标。在处理其与Three.js场景进行交互时，我们需要进行屏幕坐标和世界坐标的相互转化。
// 浏览器屏幕坐标转化成世界坐标:
//   screenToWorld(){
//     //浏览器屏幕坐标转换成标准屏幕坐标
//     let pX = (screenPoint.x / this.scene.renderer.domElement.clientWidth) * 2 - 1;
//     let pY = - (screenPoint.y / this.scene.renderer.domElement.clientHeight) * 2 + 1;
//     let p = new THREE.Vector3(pX, pY, -1).unproject(this.scene.camera)
//     return new THREE.Vector2(p.x, p.y);
//   }

// 画布
const canvas = ref<any>('');
// 场景 - 小区
const scene = new THREE.Scene();
scene.name = '小区';
// 分组 - 小区房子
const group = new THREE.Group();
group.name = '小区房子';
// 批量创建多个长方体表示高层
const group1 = new THREE.Group();
group1.name = '高层';
for (let i = 0; i < 5; i++) {
  const geometry = new THREE.BoxGeometry(20, 60, 10);
  const material = new THREE.MeshLambertMaterial({
    color: 0x00ffff
  });
  const mesh = new THREE.Mesh(geometry, material);
  mesh.position.x = i * 30; // 网格模型mesh沿着x轴方向阵列
  group1.add(mesh); // 添加到组对象group1
  mesh.name = (i + 1) + '号楼';
}
group1.position.y = 30;
// 批量创建多个长方体表示洋房
const group2 = new THREE.Group();
group2.name = '洋房';
for (let i = 0; i < 5; i++) {
  const geometry = new THREE.BoxGeometry(20, 30, 10);
  const material = new THREE.MeshLambertMaterial({
    color: 0x00ffff
  });
  const mesh = new THREE.Mesh(geometry, material);
  mesh.position.x = i * 30;
  group2.add(mesh); //添加到组对象group2
  mesh.name = i + 6 + '号楼';
}
group2.position.z = 50;
group2.position.y = 15;
// 高层 和 洋房 都属于小区房子
group.add(group1, group2);
group.position.set(-50,0,-25);
// 小区房子属于小区
scene.add(group);

// 递归遍历model包含所有的模型节点
group.traverse((obj: THREE.Mesh) => {
  const worldPosition = new THREE.Vector3();
  obj.getWorldPosition(worldPosition);
  console.log('模型节点的名称: ', obj.name, ' 世界坐标: ', worldPosition, ' 本地坐标: ', obj.position);
  // obj.isMesh：if判断模型对象obj是不是网格模型'Mesh'
  if (obj.isMesh && obj.material as THREE.MeshLambertMaterial) {// 判断条件也可以是obj.type === 'Mesh'
    const material = obj.material as THREE.MeshLambertMaterial;
    material.color.set(0xffff00);
  }
});
// 返回名.name为"4号楼"对应的对象
let mesh: THREE.Mesh = null;
const nameNode = scene.getObjectByName("4号楼");
console.log(' getObjectByName 4号楼: ', nameNode);
if (nameNode && nameNode as THREE.Mesh) {
  mesh = nameNode as THREE.Mesh;
  if (mesh.material && mesh.material as THREE.MeshLambertMaterial) {
    const lambert = mesh.material as THREE.MeshLambertMaterial;
    lambert.color.set(0xff0000);

    // 声明一个三维向量用来表示某个坐标
    const worldPosition = new THREE.Vector3();
    // 获取mesh的世界坐标，你会发现mesh的世界坐标受到父对象group的.position影响
    mesh.getWorldPosition(worldPosition);
    console.log('4号楼的世界坐标: ', worldPosition);
    console.log('4号楼的本地坐标: ', mesh.position);

    // 可视化4号楼mesh的局部坐标系
    const meshAxesHelper = new THREE.AxesHelper(50);
    mesh.add(meshAxesHelper);
  }
}
// 整个场景的局部坐标系
const sceneAxesHelper = new THREE.AxesHelper(300);
scene.add(sceneAxesHelper);
// 光源
const ambientLight = new THREE.AmbientLight(0xffffff, 1);
scene.add(ambientLight);
// 相机
const camera = new THREE.PerspectiveCamera(60, window.innerWidth / window.innerHeight, 1, 3000);
camera.position.set(200, 200, 200);
camera.lookAt(0, 0, 0);
// 渲染器
const renderer = new THREE.WebGLRenderer();
renderer.setPixelRatio(window.devicePixelRatio);
renderer.setSize(window.innerWidth, window.innerHeight);
// 渲染函数
const render = () => {
  if (mesh) {
    scene.rotation.x += 0.01;
    scene.rotation.y += 0.01;
    scene.rotation.z += 0.01;

    sceneAxesHelper.rotation.x += 0.01;
    sceneAxesHelper.rotation.y += 0.01;
    sceneAxesHelper.rotation.z += 0.01;
  }
  renderer.render(scene, camera);
  requestAnimationFrame(render);
};
// 相机控件
const orbitControl = new OrbitControls(camera, renderer.domElement);
orbitControl.update();

// 删除网格模型
// scene.remove(group2);
// 设置不可见
// group2.visible = false;
// 对于材质也可以设置是否可见, 如果多个模型使用了同一个材质，那么设置该材质的visible为false，那么将会隐藏所有使用到这个材质的模型
// material.visible = false;

onMounted(() => {
  canvas.value.appendChild(renderer.domElement);
  render();
});

</script>
